home *** CD-ROM | disk | FTP | other *** search
/ Mac Easy 2010 May / Mac Life Ubuntu.iso / casper / filesystem.squashfs / usr / lib / python2.6 / nntplib.pyc (.txt) < prev    next >
Encoding:
Python Compiled Bytecode  |  2009-04-20  |  20.5 KB  |  684 lines

  1. # Source Generated with Decompyle++
  2. # File: in.pyc (Python 2.6)
  3.  
  4. """An NNTP client class based on RFC 977: Network News Transfer Protocol.
  5.  
  6. Example:
  7.  
  8. >>> from nntplib import NNTP
  9. >>> s = NNTP('news')
  10. >>> resp, count, first, last, name = s.group('comp.lang.python')
  11. >>> print 'Group', name, 'has', count, 'articles, range', first, 'to', last
  12. Group comp.lang.python has 51 articles, range 5770 to 5821
  13. >>> resp, subs = s.xhdr('subject', first + '-' + last)
  14. >>> resp = s.quit()
  15. >>>
  16.  
  17. Here 'resp' is the server response line.
  18. Error responses are turned into exceptions.
  19.  
  20. To post an article from a file:
  21. >>> f = open(filename, 'r') # file containing article, including header
  22. >>> resp = s.post(f)
  23. >>>
  24.  
  25. For descriptions of all methods, read the comments in the code below.
  26. Note that all arguments and return values representing article numbers
  27. are strings, not numbers, since they are rarely used for calculations.
  28. """
  29. import re
  30. import socket
  31. __all__ = [
  32.     'NNTP',
  33.     'NNTPReplyError',
  34.     'NNTPTemporaryError',
  35.     'NNTPPermanentError',
  36.     'NNTPProtocolError',
  37.     'NNTPDataError',
  38.     'error_reply',
  39.     'error_temp',
  40.     'error_perm',
  41.     'error_proto',
  42.     'error_data']
  43.  
  44. class NNTPError(Exception):
  45.     '''Base class for all nntplib exceptions'''
  46.     
  47.     def __init__(self, *args):
  48.         Exception.__init__(self, *args)
  49.         
  50.         try:
  51.             self.response = args[0]
  52.         except IndexError:
  53.             self.response = 'No response given'
  54.  
  55.  
  56.  
  57.  
  58. class NNTPReplyError(NNTPError):
  59.     '''Unexpected [123]xx reply'''
  60.     pass
  61.  
  62.  
  63. class NNTPTemporaryError(NNTPError):
  64.     '''4xx errors'''
  65.     pass
  66.  
  67.  
  68. class NNTPPermanentError(NNTPError):
  69.     '''5xx errors'''
  70.     pass
  71.  
  72.  
  73. class NNTPProtocolError(NNTPError):
  74.     '''Response does not begin with [1-5]'''
  75.     pass
  76.  
  77.  
  78. class NNTPDataError(NNTPError):
  79.     '''Error in response data'''
  80.     pass
  81.  
  82. error_reply = NNTPReplyError
  83. error_temp = NNTPTemporaryError
  84. error_perm = NNTPPermanentError
  85. error_proto = NNTPProtocolError
  86. error_data = NNTPDataError
  87. NNTP_PORT = 119
  88. LONGRESP = [
  89.     '100',
  90.     '215',
  91.     '220',
  92.     '221',
  93.     '222',
  94.     '224',
  95.     '230',
  96.     '231',
  97.     '282']
  98. CRLF = '\r\n'
  99.  
  100. class NNTP:
  101.     
  102.     def __init__(self, host, port = NNTP_PORT, user = None, password = None, readermode = None, usenetrc = True):
  103.         """Initialize an instance.  Arguments:
  104.         - host: hostname to connect to
  105.         - port: port to connect to (default the standard NNTP port)
  106.         - user: username to authenticate with
  107.         - password: password to use with username
  108.         - readermode: if true, send 'mode reader' command after
  109.                       connecting.
  110.  
  111.         readermode is sometimes necessary if you are connecting to an
  112.         NNTP server on the local machine and intend to call
  113.         reader-specific comamnds, such as `group'.  If you get
  114.         unexpected NNTPPermanentErrors, you might need to set
  115.         readermode.
  116.         """
  117.         self.host = host
  118.         self.port = port
  119.         self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  120.         self.sock.connect((self.host, self.port))
  121.         self.file = self.sock.makefile('rb')
  122.         self.debugging = 0
  123.         self.welcome = self.getresp()
  124.         readermode_afterauth = 0
  125.         if readermode:
  126.             
  127.             try:
  128.                 self.welcome = self.shortcmd('mode reader')
  129.             except NNTPPermanentError:
  130.                 pass
  131.             except NNTPTemporaryError:
  132.                 e = None
  133.                 if user and e.response[:3] == '480':
  134.                     readermode_afterauth = 1
  135.                 else:
  136.                     raise 
  137.                 e.response[:3] == '480'
  138.             
  139.  
  140.         None<EXCEPTION MATCH>NNTPPermanentError
  141.         
  142.         try:
  143.             if usenetrc and not user:
  144.                 import netrc
  145.                 credentials = netrc.netrc()
  146.                 auth = credentials.authenticators(host)
  147.                 if auth:
  148.                     user = auth[0]
  149.                     password = auth[2]
  150.                 
  151.         except IOError:
  152.             pass
  153.  
  154.         if user:
  155.             resp = self.shortcmd('authinfo user ' + user)
  156.             if resp[:3] == '381':
  157.                 if not password:
  158.                     raise NNTPReplyError(resp)
  159.                 password
  160.                 resp = self.shortcmd('authinfo pass ' + password)
  161.                 if resp[:3] != '281':
  162.                     raise NNTPPermanentError(resp)
  163.                 resp[:3] != '281'
  164.             
  165.             if readermode_afterauth:
  166.                 
  167.                 try:
  168.                     self.welcome = self.shortcmd('mode reader')
  169.                 except NNTPPermanentError:
  170.                     pass
  171.                 except:
  172.                     None<EXCEPTION MATCH>NNTPPermanentError
  173.                 
  174.  
  175.             None<EXCEPTION MATCH>NNTPPermanentError
  176.         
  177.  
  178.     
  179.     def getwelcome(self):
  180.         '''Get the welcome message from the server
  181.         (this is read and squirreled away by __init__()).
  182.         If the response code is 200, posting is allowed;
  183.         if it 201, posting is not allowed.'''
  184.         if self.debugging:
  185.             print '*welcome*', repr(self.welcome)
  186.         
  187.         return self.welcome
  188.  
  189.     
  190.     def set_debuglevel(self, level):
  191.         """Set the debugging level.  Argument 'level' means:
  192.         0: no debugging output (default)
  193.         1: print commands and responses but not body text etc.
  194.         2: also print raw lines read and sent before stripping CR/LF"""
  195.         self.debugging = level
  196.  
  197.     debug = set_debuglevel
  198.     
  199.     def putline(self, line):
  200.         '''Internal: send one line to the server, appending CRLF.'''
  201.         line = line + CRLF
  202.         if self.debugging > 1:
  203.             print '*put*', repr(line)
  204.         
  205.         self.sock.sendall(line)
  206.  
  207.     
  208.     def putcmd(self, line):
  209.         '''Internal: send one command to the server (through putline()).'''
  210.         if self.debugging:
  211.             print '*cmd*', repr(line)
  212.         
  213.         self.putline(line)
  214.  
  215.     
  216.     def getline(self):
  217.         '''Internal: return one line from the server, stripping CRLF.
  218.         Raise EOFError if the connection is closed.'''
  219.         line = self.file.readline()
  220.         if self.debugging > 1:
  221.             print '*get*', repr(line)
  222.         
  223.         if not line:
  224.             raise EOFError
  225.         line
  226.         if line[-2:] == CRLF:
  227.             line = line[:-2]
  228.         elif line[-1:] in CRLF:
  229.             line = line[:-1]
  230.         
  231.         return line
  232.  
  233.     
  234.     def getresp(self):
  235.         '''Internal: get a response from the server.
  236.         Raise various errors if the response indicates an error.'''
  237.         resp = self.getline()
  238.         if self.debugging:
  239.             print '*resp*', repr(resp)
  240.         
  241.         c = resp[:1]
  242.         if c == '4':
  243.             raise NNTPTemporaryError(resp)
  244.         c == '4'
  245.         if c == '5':
  246.             raise NNTPPermanentError(resp)
  247.         c == '5'
  248.         if c not in '123':
  249.             raise NNTPProtocolError(resp)
  250.         c not in '123'
  251.         return resp
  252.  
  253.     
  254.     def getlongresp(self, file = None):
  255.         '''Internal: get a response plus following text from the server.
  256.         Raise various errors if the response indicates an error.'''
  257.         openedFile = None
  258.         
  259.         try:
  260.             if isinstance(file, str):
  261.                 openedFile = file = open(file, 'w')
  262.             
  263.             resp = self.getresp()
  264.             if resp[:3] not in LONGRESP:
  265.                 raise NNTPReplyError(resp)
  266.             resp[:3] not in LONGRESP
  267.             list = []
  268.             while None:
  269.                 line = self.getline()
  270.                 if line == '.':
  271.                     break
  272.                 
  273.                 if line[:2] == '..':
  274.                     line = line[1:]
  275.                 
  276.                 if file:
  277.                     file.write(line + '\n')
  278.                     continue
  279.                 list.append(line)
  280.             if openedFile:
  281.                 openedFile.close()
  282.             
  283.             return (resp, list)
  284.  
  285.  
  286.     
  287.     def shortcmd(self, line):
  288.         '''Internal: send a command and get the response.'''
  289.         self.putcmd(line)
  290.         return self.getresp()
  291.  
  292.     
  293.     def longcmd(self, line, file = None):
  294.         '''Internal: send a command and get the response plus following text.'''
  295.         self.putcmd(line)
  296.         return self.getlongresp(file)
  297.  
  298.     
  299.     def newgroups(self, date, time, file = None):
  300.         """Process a NEWGROUPS command.  Arguments:
  301.         - date: string 'yymmdd' indicating the date
  302.         - time: string 'hhmmss' indicating the time
  303.         Return:
  304.         - resp: server response if successful
  305.         - list: list of newsgroup names"""
  306.         return self.longcmd('NEWGROUPS ' + date + ' ' + time, file)
  307.  
  308.     
  309.     def newnews(self, group, date, time, file = None):
  310.         """Process a NEWNEWS command.  Arguments:
  311.         - group: group name or '*'
  312.         - date: string 'yymmdd' indicating the date
  313.         - time: string 'hhmmss' indicating the time
  314.         Return:
  315.         - resp: server response if successful
  316.         - list: list of message ids"""
  317.         cmd = 'NEWNEWS ' + group + ' ' + date + ' ' + time
  318.         return self.longcmd(cmd, file)
  319.  
  320.     
  321.     def list(self, file = None):
  322.         '''Process a LIST command.  Return:
  323.         - resp: server response if successful
  324.         - list: list of (group, last, first, flag) (strings)'''
  325.         (resp, list) = self.longcmd('LIST', file)
  326.         for i in range(len(list)):
  327.             list[i] = tuple(list[i].split())
  328.         
  329.         return (resp, list)
  330.  
  331.     
  332.     def description(self, group):
  333.         """Get a description for a single group.  If more than one
  334.         group matches ('group' is a pattern), return the first.  If no
  335.         group matches, return an empty string.
  336.  
  337.         This elides the response code from the server, since it can
  338.         only be '215' or '285' (for xgtitle) anyway.  If the response
  339.         code is needed, use the 'descriptions' method.
  340.  
  341.         NOTE: This neither checks for a wildcard in 'group' nor does
  342.         it check whether the group actually exists."""
  343.         (resp, lines) = self.descriptions(group)
  344.         if len(lines) == 0:
  345.             return ''
  346.         return lines[0][1]
  347.  
  348.     
  349.     def descriptions(self, group_pattern):
  350.         '''Get descriptions for a range of groups.'''
  351.         line_pat = re.compile('^(?P<group>[^ \t]+)[ \t]+(.*)$')
  352.         (resp, raw_lines) = self.longcmd('LIST NEWSGROUPS ' + group_pattern)
  353.         if resp[:3] != '215':
  354.             (resp, raw_lines) = self.longcmd('XGTITLE ' + group_pattern)
  355.         
  356.         lines = []
  357.         for raw_line in raw_lines:
  358.             match = line_pat.search(raw_line.strip())
  359.             if match:
  360.                 lines.append(match.group(1, 2))
  361.                 continue
  362.         
  363.         return (resp, lines)
  364.  
  365.     
  366.     def group(self, name):
  367.         '''Process a GROUP command.  Argument:
  368.         - group: the group name
  369.         Returns:
  370.         - resp: server response if successful
  371.         - count: number of articles (string)
  372.         - first: first article number (string)
  373.         - last: last article number (string)
  374.         - name: the group name'''
  375.         resp = self.shortcmd('GROUP ' + name)
  376.         if resp[:3] != '211':
  377.             raise NNTPReplyError(resp)
  378.         resp[:3] != '211'
  379.         words = resp.split()
  380.         count = first = last = 0
  381.         n = len(words)
  382.         if n > 1:
  383.             count = words[1]
  384.             if n > 2:
  385.                 first = words[2]
  386.                 if n > 3:
  387.                     last = words[3]
  388.                     if n > 4:
  389.                         name = words[4].lower()
  390.                     
  391.                 
  392.             
  393.         
  394.         return (resp, count, first, last, name)
  395.  
  396.     
  397.     def help(self, file = None):
  398.         '''Process a HELP command.  Returns:
  399.         - resp: server response if successful
  400.         - list: list of strings'''
  401.         return self.longcmd('HELP', file)
  402.  
  403.     
  404.     def statparse(self, resp):
  405.         '''Internal: parse the response of a STAT, NEXT or LAST command.'''
  406.         if resp[:2] != '22':
  407.             raise NNTPReplyError(resp)
  408.         resp[:2] != '22'
  409.         words = resp.split()
  410.         nr = 0
  411.         id = ''
  412.         n = len(words)
  413.         if n > 1:
  414.             nr = words[1]
  415.             if n > 2:
  416.                 id = words[2]
  417.             
  418.         
  419.         return (resp, nr, id)
  420.  
  421.     
  422.     def statcmd(self, line):
  423.         '''Internal: process a STAT, NEXT or LAST command.'''
  424.         resp = self.shortcmd(line)
  425.         return self.statparse(resp)
  426.  
  427.     
  428.     def stat(self, id):
  429.         '''Process a STAT command.  Argument:
  430.         - id: article number or message id
  431.         Returns:
  432.         - resp: server response if successful
  433.         - nr:   the article number
  434.         - id:   the message id'''
  435.         return self.statcmd('STAT ' + id)
  436.  
  437.     
  438.     def next(self):
  439.         '''Process a NEXT command.  No arguments.  Return as for STAT.'''
  440.         return self.statcmd('NEXT')
  441.  
  442.     
  443.     def last(self):
  444.         '''Process a LAST command.  No arguments.  Return as for STAT.'''
  445.         return self.statcmd('LAST')
  446.  
  447.     
  448.     def artcmd(self, line, file = None):
  449.         '''Internal: process a HEAD, BODY or ARTICLE command.'''
  450.         (resp, list) = self.longcmd(line, file)
  451.         (resp, nr, id) = self.statparse(resp)
  452.         return (resp, nr, id, list)
  453.  
  454.     
  455.     def head(self, id):
  456.         """Process a HEAD command.  Argument:
  457.         - id: article number or message id
  458.         Returns:
  459.         - resp: server response if successful
  460.         - nr: article number
  461.         - id: message id
  462.         - list: the lines of the article's header"""
  463.         return self.artcmd('HEAD ' + id)
  464.  
  465.     
  466.     def body(self, id, file = None):
  467.         """Process a BODY command.  Argument:
  468.         - id: article number or message id
  469.         - file: Filename string or file object to store the article in
  470.         Returns:
  471.         - resp: server response if successful
  472.         - nr: article number
  473.         - id: message id
  474.         - list: the lines of the article's body or an empty list
  475.                 if file was used"""
  476.         return self.artcmd('BODY ' + id, file)
  477.  
  478.     
  479.     def article(self, id):
  480.         '''Process an ARTICLE command.  Argument:
  481.         - id: article number or message id
  482.         Returns:
  483.         - resp: server response if successful
  484.         - nr: article number
  485.         - id: message id
  486.         - list: the lines of the article'''
  487.         return self.artcmd('ARTICLE ' + id)
  488.  
  489.     
  490.     def slave(self):
  491.         '''Process a SLAVE command.  Returns:
  492.         - resp: server response if successful'''
  493.         return self.shortcmd('SLAVE')
  494.  
  495.     
  496.     def xhdr(self, hdr, str, file = None):
  497.         """Process an XHDR command (optional server extension).  Arguments:
  498.         - hdr: the header type (e.g. 'subject')
  499.         - str: an article nr, a message id, or a range nr1-nr2
  500.         Returns:
  501.         - resp: server response if successful
  502.         - list: list of (nr, value) strings"""
  503.         pat = re.compile('^([0-9]+) ?(.*)\n?')
  504.         (resp, lines) = self.longcmd('XHDR ' + hdr + ' ' + str, file)
  505.         for i in range(len(lines)):
  506.             line = lines[i]
  507.             m = pat.match(line)
  508.             if m:
  509.                 lines[i] = m.group(1, 2)
  510.                 continue
  511.         
  512.         return (resp, lines)
  513.  
  514.     
  515.     def xover(self, start, end, file = None):
  516.         '''Process an XOVER command (optional server extension) Arguments:
  517.         - start: start of range
  518.         - end: end of range
  519.         Returns:
  520.         - resp: server response if successful
  521.         - list: list of (art-nr, subject, poster, date,
  522.                          id, references, size, lines)'''
  523.         (resp, lines) = self.longcmd('XOVER ' + start + '-' + end, file)
  524.         xover_lines = []
  525.         for line in lines:
  526.             elem = line.split('\t')
  527.             
  528.             try:
  529.                 xover_lines.append((elem[0], elem[1], elem[2], elem[3], elem[4], elem[5].split(), elem[6], elem[7]))
  530.             continue
  531.             except IndexError:
  532.                 raise NNTPDataError(line)
  533.                 continue
  534.             
  535.  
  536.         
  537.         return (resp, xover_lines)
  538.  
  539.     
  540.     def xgtitle(self, group, file = None):
  541.         '''Process an XGTITLE command (optional server extension) Arguments:
  542.         - group: group name wildcard (i.e. news.*)
  543.         Returns:
  544.         - resp: server response if successful
  545.         - list: list of (name,title) strings'''
  546.         line_pat = re.compile('^([^ \t]+)[ \t]+(.*)$')
  547.         (resp, raw_lines) = self.longcmd('XGTITLE ' + group, file)
  548.         lines = []
  549.         for raw_line in raw_lines:
  550.             match = line_pat.search(raw_line.strip())
  551.             if match:
  552.                 lines.append(match.group(1, 2))
  553.                 continue
  554.         
  555.         return (resp, lines)
  556.  
  557.     
  558.     def xpath(self, id):
  559.         '''Process an XPATH command (optional server extension) Arguments:
  560.         - id: Message id of article
  561.         Returns:
  562.         resp: server response if successful
  563.         path: directory path to article'''
  564.         resp = self.shortcmd('XPATH ' + id)
  565.         if resp[:3] != '223':
  566.             raise NNTPReplyError(resp)
  567.         resp[:3] != '223'
  568.         
  569.         try:
  570.             (resp_num, path) = resp.split()
  571.         except ValueError:
  572.             raise NNTPReplyError(resp)
  573.  
  574.         return (resp, path)
  575.  
  576.     
  577.     def date(self):
  578.         '''Process the DATE command. Arguments:
  579.         None
  580.         Returns:
  581.         resp: server response if successful
  582.         date: Date suitable for newnews/newgroups commands etc.
  583.         time: Time suitable for newnews/newgroups commands etc.'''
  584.         resp = self.shortcmd('DATE')
  585.         if resp[:3] != '111':
  586.             raise NNTPReplyError(resp)
  587.         resp[:3] != '111'
  588.         elem = resp.split()
  589.         if len(elem) != 2:
  590.             raise NNTPDataError(resp)
  591.         len(elem) != 2
  592.         date = elem[1][2:8]
  593.         time = elem[1][-6:]
  594.         if len(date) != 6 or len(time) != 6:
  595.             raise NNTPDataError(resp)
  596.         len(time) != 6
  597.         return (resp, date, time)
  598.  
  599.     
  600.     def post(self, f):
  601.         '''Process a POST command.  Arguments:
  602.         - f: file containing the article
  603.         Returns:
  604.         - resp: server response if successful'''
  605.         resp = self.shortcmd('POST')
  606.         if resp[0] != '3':
  607.             raise NNTPReplyError(resp)
  608.         resp[0] != '3'
  609.         while None:
  610.             line = f.readline()
  611.             if not line:
  612.                 break
  613.             
  614.             if line[-1] == '\n':
  615.                 line = line[:-1]
  616.             
  617.             if line[:1] == '.':
  618.                 line = '.' + line
  619.             
  620.             continue
  621.             self.putline('.')
  622.             return self.getresp()
  623.  
  624.     
  625.     def ihave(self, id, f):
  626.         '''Process an IHAVE command.  Arguments:
  627.         - id: message-id of the article
  628.         - f:  file containing the article
  629.         Returns:
  630.         - resp: server response if successful
  631.         Note that if the server refuses the article an exception is raised.'''
  632.         resp = self.shortcmd('IHAVE ' + id)
  633.         if resp[0] != '3':
  634.             raise NNTPReplyError(resp)
  635.         resp[0] != '3'
  636.         while None:
  637.             line = f.readline()
  638.             if not line:
  639.                 break
  640.             
  641.             if line[-1] == '\n':
  642.                 line = line[:-1]
  643.             
  644.             if line[:1] == '.':
  645.                 line = '.' + line
  646.             
  647.             continue
  648.             self.putline('.')
  649.             return self.getresp()
  650.  
  651.     
  652.     def quit(self):
  653.         '''Process a QUIT command and close the socket.  Returns:
  654.         - resp: server response if successful'''
  655.         resp = self.shortcmd('QUIT')
  656.         self.file.close()
  657.         self.sock.close()
  658.         del self.file
  659.         del self.sock
  660.         return resp
  661.  
  662.  
  663. if __name__ == '__main__':
  664.     import os
  665.     if 'news':
  666.         pass
  667.     newshost = os.environ['NNTPSERVER']
  668.     if newshost.find('.') == -1:
  669.         mode = 'readermode'
  670.     else:
  671.         mode = None
  672.     s = NNTP(newshost, readermode = mode)
  673.     (resp, count, first, last, name) = s.group('comp.lang.python')
  674.     print resp
  675.     print 'Group', name, 'has', count, 'articles, range', first, 'to', last
  676.     (resp, subs) = s.xhdr('subject', first + '-' + last)
  677.     print resp
  678.     for item in subs:
  679.         print '%7s %s' % item
  680.     
  681.     resp = s.quit()
  682.     print resp
  683.  
  684.